/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package fs.coordenador;

import fs.coordenador.util.Util;
import java.io.*;
import java.net.*;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Servidor com threads por conexão. As threads são criadas assim que as
 * conexões são feitas. A thread criada processa o pedido, responde ao cliente e
 * é finalizada.
 *
 * @author Manoel Afonso Filho
 */
public class Servidor extends Thread {

    private Socket socketCliente;
    
    public static Queue<Servidor> fila;
    
    public static boolean bloqueado = false;

    private String menu = "Bem vindo ao sistema de arquivos distribuído.\n"
            + "Escolha uma opção:\n"
            + "1- Criar arquivo: <nome_arquivo>\n"
            + "2- Apagar arquivo: <nome_arquivo>\n"
            + "3- Escrever arquivo: <nome_arquivo>\n"
            + "4- Ler arquivo: <nome_arquivo>\n"
            + "5- Listar arquivos\n"
            + "6- Sair";

    private String alerta = "Bem vindo ao sistema de arquivos distribuído.\n"
            + "Favor aguardar até que o diretório seja liberado.";

    public Servidor(Socket socket) {
        this.socketCliente = socket;
    }

    @Override
    public void run() {
        //Bloqueia os recursos
        Servidor.bloqueado = true;
        
        ObjectOutputStream saida = null;
        ObjectInputStream entrada = null;
        
        
        try {
            //Obtém os fluxos de entrada e saída.            
            saida = new ObjectOutputStream(socketCliente.getOutputStream());
            saida.flush();
            entrada = new ObjectInputStream(socketCliente.getInputStream());
            
            //Envia mensagem de boas-vindas assim que o cliente se conecta
            enviarMensagem(menu, saida);
            
            boolean sair = false;
            
            while (!sair) {
                //Aguarda até que o cliente envie um pedido.
                entrada.read();
                
                //Lê o objeto enviado pelo cliente.
                Object obj = entrada.readObject();
                if (obj != null && obj instanceof String) {
                    //Comando na forma <opcao> <arquivo>
                    String comando = (String) obj;
                    
                    //Separa a opção do nome do arquivo
                    String opcao = "";
                    String arquivo = "";
                    String texto = "";
                    
                    Pattern r = Pattern.compile("([0-9])\\s?(\"(.*?)\")?\\s?(\"(.*?)\")?");
                    Matcher m = r.matcher(comando);
                    if(m.find()){
                        opcao = m.group(1);
                        arquivo = m.group(3);
                        texto = m.group(5);
                    }
                    
                    switch (opcao) {
                        case "1":
                            //Criar arquivo
                            Util.criarArquivo(arquivo);
                            enviarMensagem("Arquivo criado", saida);
                            break;
                        case "2":
                            //Apagar arquivo
                            Util.apagarArquivo(arquivo);
                            enviarMensagem("Arquivo apagado", saida);
                            break;
                        case "3":
                            //Escreve texto no arquivo
                            Util.escreverArquivo(arquivo, texto);
                            enviarMensagem("Texto gravado", saida);
                            break;
                        case "4":
                            //Mostra o conteúdo do arquivo para o cliente
                            String conteudo = "---------- Conteúdo ----------\n"
                                    + Util.lerArquivo(arquivo)
                                    + "-----------------------------";
                            enviarMensagem(conteudo, saida);
                            break;
                        case "5":
                            //Lista os arquivos presentes
                            enviarMensagem(Util.listar(), saida);
                            break;
                        case "6":
                            //Se desconecta do servidor
                            sair = true;
                            //Desbloqueia os recursos
                            Servidor.bloqueado = false;
                            //Manda executar o próximo da fila
                            executarProximo();
                            //Manda mensagem de despedida
                            enviarMensagem("bye", saida);
                            break;
                    }
                }
            }

        } catch (Exception ex) {
            System.err.println("Erro: " + ex);
        } finally {
            //Finaliza a conexão com o cliente.
            try {
                entrada.close();
                saida.close();
            } catch (IOException ex) {
                System.err.println("Erro: " + ex);
            }
        }
    }
    
    
    private void printBloqueado(){
        System.out.println("Bem vindo ao sistema de arquivos distribuído.");
        System.out.println("Favor aguardar até que o diretório seja liberado.");
    }

    /**
     * Envia um objeto para o cliente.
     *
     * @param msg Objeto a ser enviado.
     */
    private void enviarMensagem(Object msg, ObjectOutputStream saida) {
        try {
            saida.writeObject(msg);
            saida.flush();
        } catch (IOException ex) {
            System.err.println("Erro ao enviar \"" + msg + "\" : " + ex);
        }
    }
    
    /**
     * Após um cliente ter desbloqueado os recursos, executa o próximo da fila.
     */
    public static void executarProximo(){
        if(!fila.isEmpty()){
            Servidor cliente = fila.remove();
            cliente.start();
        }
    }

    /**
     * Cria o servidor.
     *
     * @param args
     * @throws IOException
     */
    public static void main(String args[]) throws IOException {
        System.out.println("Iniciando servidor coordenador.");
        
        ServerSocket socketServidor = new ServerSocket(2004);
        System.out.println("Servidor aguardando conexões.");
        //Aguarda uma conexão.
        Socket socketCliente = socketServidor.accept();
        
        fila = new ConcurrentLinkedQueue();
        
        while (true) {
            //Cria uma thread para processar o ordenamento.
            Servidor servidor = new Servidor(socketCliente);
            
            if(!Servidor.bloqueado){
                //Se os recursos não estão bloqueados, permite o cliente usá-los
                servidor.start();
            } else{
                //Se estão bloqueados, aguarda na fila.
                fila.add(servidor);
            }
            
            //Aguarda outra conexão para criar outra thread.
            socketCliente = socketServidor.accept();
        }
    }
}